package com.sakura.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sakura.seckill.config.AccessLimit;
import com.sakura.seckill.exception.GlobalException;
import com.sakura.seckill.pojo.Order;
import com.sakura.seckill.pojo.SeckillMessage;
import com.sakura.seckill.pojo.SeckillOrder;
import com.sakura.seckill.pojo.User;

import com.sakura.seckill.rabbitmq.MQSender;
import com.sakura.seckill.service.IGoodsService;
import com.sakura.seckill.service.ISeckillOrderService;
import com.sakura.seckill.utils.JsonUtil;
import com.sakura.seckill.vo.GoodsVo;
import com.sakura.seckill.vo.RespBean;
import com.sakura.seckill.vo.RespBeanEnum;
import com.wf.captcha.ArithmeticCaptcha;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * 秒杀功能
 * @author Sakura
 * @Date 2022-08-17 11:35
 */
@Slf4j
@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ThymeleafViewResolver thymeleafViewResolver;
    @Autowired
    private MQSender mqSender;
    @Autowired
    private RedisScript redisScript;

    //内存标记
    private Map<Long,Boolean> EmptyStockMap =new HashMap<>();

    /**
     * 功能描述：秒杀功能
     *
     *     压力测试：15w次  5000*10*3
     *     windows优化前QPS：626/s
     *              缓存QPS:1685/s
     *          接口优化QPS:2366/s
     *
     *     Linus 优化前QPS: 278
     *
     * @param model
     * @param user
     * @param goodsId
     * @return
     */
      @AccessLimit(second = 5,maxCount = 5,needLogin = true)
    @PostMapping("/{path}/doSeckill")
    @ResponseBody
    public String doSeckill(@PathVariable String path, Model model, User user,Long goodsId){



        if(user == null){
            return "login";
        }
        ValueOperations valueOperations = redisTemplate.opsForValue();

        //判断接口地址是否正确
        boolean check = seckillOrderService.checkPath(user,goodsId,path);
        /*if(!check){
            model.addAttribute("errmsg", RespBeanEnum.REQUEST_ILLEGAL.getMessage());
            return "secKillFail";
        }*/

        model.addAttribute("user",user);

        //通过商品id查询商品信息
        GoodsVo goods = goodsService.findGoodVoByGoodsId(goodsId);


        

        //内存标记，减少Redis的访问
        if(EmptyStockMap.get(goodsId)){
            model.addAttribute("errmsg",RespBeanEnum.EMPTY_STOCK.getMessage());
            return "secKillFail";
        }

        //Redis预减库存
        Long stock = valueOperations.decrement("seckillGoods:" + goodsId);

        //使用lua脚本
        /*Long stock = (Long) redisTemplate.execute(redisScript, Collections.singletonList(
                        "seckillGoods:" + goodsId), Collections.emptyList());*/

        if(stock<0){
            EmptyStockMap.put(goodsId,true);
            valueOperations.increment("seckillGoods:" + goodsId);

            model.addAttribute("errmsg",RespBeanEnum.EMPTY_STOCK.getMessage());
            return "secKillFail";
        }

        //rabbit发送信息
        SeckillMessage seckillMessage=new SeckillMessage(user,goodsId);
        mqSender.sendSeckillMessage(JsonUtil.object2JsonStr(seckillMessage));

        //秒杀
        Order order=seckillOrderService.seckill(user,goods);

        //秒杀成功，删除缓存
        if (order!=null){
            redisTemplate.delete("goodsList");
        }

        //如果库存缓存为0，删除缓存
        /*Long stockEmpty = (Long) valueOperations.get("isStockEmpty:" + goodsId);
        if (stockEmpty == 0 ){
            redisTemplate.delete("seckillGoods:"+ goodsId);
        }*/

        model.addAttribute("order",order);
        model.addAttribute("goods",goods);

        return "orderDetail";
    }


    /**
     * 功能描述：获取秒杀地址
     *
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "/path",method = RequestMethod.GET)
    @ResponseBody
    public RespBean getPath(User user,Long goodsId,String captcha){
        if(user==null){
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//            model.addAttribute("errmsg", RespBeanEnum.SESSION_ERROR.getMessage());
//            return "secKillFail";
        }
        //校验验证码
        boolean check=seckillOrderService.checkCaptcha(user,goodsId,captcha);
        if(!check){
            return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
        }

        String strPath = seckillOrderService.createPath(user,goodsId);
        return RespBean.success(strPath);
    }

    /**
     * 功能秒杀：生成验证码
     * @param user
     * @param goodsId
     * @param response
     */
    @RequestMapping(value = "/captcha",method = RequestMethod.GET)
    public void captcha(User user,Long goodsId,HttpServletResponse response){
        if (user ==null){
            throw new GlobalException(RespBeanEnum.REQUEST_ILLEGAL);
        }

        // 设置请求头为输出图片类型
        response.setContentType("image/gif");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        //生成验证码，将结果放入Redis中,过期时间为300s
        //使用算术类型，三个参数分别为宽、高、位数
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 32,3);
        redisTemplate.opsForValue().set("captcha:"+user.getId()+":"+goodsId,captcha.text(),
                                        300,TimeUnit.SECONDS);
        try {
            //输出验证码
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            log.info("验证码生成失败，{}",e.getMessage());
        }
    }

    /**
     * 系统初始化，把商品数量加载到Redis中
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {

        List<GoodsVo> list = goodsService.findGoodVo();
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        list.forEach(goodsVo -> {
            redisTemplate.opsForValue().set("seckillGoods:"+goodsVo.getId(),goodsVo.getStockCount());
            EmptyStockMap.put(goodsVo.getId(),false);
        });

    }

    /**
     * 功能描述：获取秒杀结果
     *
     * orderId：成功：
     *      -1：失败
     *       0：排队中
     */
    @RequestMapping(value = "/result",method = RequestMethod.GET)
    @ResponseBody
    public Long getResult(User user,Long goodsId){
        if(user==null){
            return -1L;
        }
        Long orderId=seckillOrderService.getResult(user,goodsId);
        return orderId;
    }


}
